package io.kiki.stack.netty.protocol;

import io.kiki.stack.netty.command.CommandFactory;
import io.kiki.stack.netty.command.CommandFactoryImpl;
import io.kiki.stack.netty.command.CommandHandler;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;

/**
 * Request command protocol for v2
 * 0     1     2           4           6           8          10     11     12          14         16
 * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+-----+-----+-----+
 * |proto| ver1|type | cmdcode   |ver2 |   requestId           |codec|switch|   timeout             |
 * +-----------+-----------+-----------+-----------+-----------+------------+-----------+-----------+
 * |classLen   |headerLen  |contentLen             |           ...                                  |
 * +-----------+-----------+-----------+-----------+                                                +
 * |               className + header  + content  bytes                                             |
 * +                                                                                                +
 * |                               ... ...                                  | CRC32(optional)       |
 * +------------------------------------------------------------------------------------------------+
 * <p>
 * proto: code for protocol
 * ver1: version for protocol
 * type: request/response/request oneway
 * cmdcode: code for remoting command
 * ver2:version for remoting command
 * requestId: id of request
 * codec: code for codec
 * switch: function switch for protocol
 * headerLen: length of header
 * contentLen: length of content
 * CRC32: CRC32 of the frame(Exists when ver1 > 1)
 * <p>
 * Response command protocol for v2
 * 0     1     2     3     4           6           8          10     11    12          14          16
 * +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+------+-----+-----+-----+-----+
 * |proto| ver1| type| cmdcode   |ver2 |   requestId           |codec|switch|respstatus |  classLen |
 * +-----------+-----------+-----------+-----------+-----------+------------+-----------+-----------+
 * |headerLen  | contentLen            |                      ...                                   |
 * +-----------------------------------+                                                            +
 * |               className + header  + content  bytes                                             |
 * +                                                                                                +
 * |                               ... ...                                  | CRC32(optional)       |
 * +------------------------------------------------------------------------------------------------+
 * respstatus: response status
 */

@Slf4j
@Setter
@Getter
public class ProtocolImpl implements Protocol {


    public static final Protocol protocol = new ProtocolImpl();
    /**
     * in contrast to protocol v1,
     * one more byte is used as protocol version,
     * and another one is userd as protocol switch
     */
    private static final int REQUEST_HEADER_LEN = 22 + 2;
    private static final int RESPONSE_HEADER_LEN = 20 + 2;
    private final Encoder encoder;
    private final Decoder decoder;
    private final HeartbeatTrigger heartbeatTrigger;
    private final CommandHandler commandHandler;
    private final CommandFactory commandFactory;

    public ProtocolImpl() {
        this.encoder = new EncoderImpl();
        this.decoder = new DecoderImpl();
        this.commandFactory = new CommandFactoryImpl();
        this.heartbeatTrigger = new RpcHeartbeatTrigger(this.commandFactory);
        this.commandHandler = new RpcCommandHandler(this.commandFactory);
    }

    public static int getRequestHeaderLength() {
        return ProtocolImpl.REQUEST_HEADER_LEN;
    }

    public static int getResponseHeaderLength() {
        return ProtocolImpl.RESPONSE_HEADER_LEN;
    }

    @Override
    public Encoder getEncoder() {
        return this.encoder;
    }

    @Override
    public Decoder getDecoder() {
        return this.decoder;
    }

    @Override
    public HeartbeatTrigger getHeartbeatTrigger() {
        return this.heartbeatTrigger;
    }

    @Override
    public CommandHandler getCommandHandler() {
        return this.commandHandler;
    }

    @Override
    public CommandFactory getCommandFactory() {
        return this.commandFactory;
    }
}
